using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    public class SNNHardware : RapidHardware
    {
        PipelineSerialAssembler oAssembler;
        public override bool ConfigureSettings()
        {
            Settings.UseStart = false;
            Settings.BuildHardware = true;
            Settings.ForceBuildHardware = false;
            Settings.RunFromXilinx = true;
            Settings.ClockFrequency = 10;
            Settings.UseSDRAM = true;
            Settings.BuildRemote = false;
            return true;
        }

        SNNMaster oSNN;

        public override bool GenerateStructure(TopLevelComponent TopLevelComponent)
        {
            // why required?
            TopLevelComponent.SDRAMInterface.Enabled = true;
            TopLevelComponent.FIFOInterface.Enabled = true;
            TopLevelComponent.TestBusWrite.Join(0);


            //--------  FLOW PC to SDRAM

            PipelineBuffer oInBuffer = new PipelineBuffer(TopLevelComponent, "in_buffer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 18, 4);
            oInBuffer.InputBus.Join(TopLevelComponent.FIFOInterface.BusFromExternal);
            oAssembler = new PipelineSerialAssembler(TopLevelComponent, "assembler", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 18, 16, 2, 4);
            oAssembler.InputBus.Join(oInBuffer.OutputBus);

            PipelineSorter oPCSoter = new PipelineSorter(TopLevelComponent, "pc_sort", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 64, 62, 1);
            oPCSoter.InputBus.Join(oAssembler.OutputBus);
            PipelineBuffer oPCMemBuffer = new PipelineBuffer(TopLevelComponent, "pc_mem_buffer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 64, 4);
            oPCMemBuffer.InputBus.Join(oPCSoter.DefineOutput("PC_O", 1));
            PipelineBuffer oPCFPGABuffer = new PipelineBuffer(TopLevelComponent, "pc_fpga_buffer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 64, 1);
            oPCFPGABuffer.InputBus.Join(oPCSoter.DefineOutput("FPGA_O", 0));

            PipelineBuffer oFPGAMemBuffer = new PipelineBuffer(TopLevelComponent, "fpga_mem_buffer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 64, 4);

            PipelineMergerPriority oMemMerger = new PipelineMergerPriority(TopLevelComponent, "sdram_merger", 64, TopLevelComponent.MainClock, TopLevelComponent.MainReset, 2);
            oMemMerger.InputBuses[0].Join(oPCMemBuffer.OutputBus);
            oMemMerger.InputBuses[1].Join(oFPGAMemBuffer.OutputBus);
                        
            PipelineStage oMemIsolationBufferIn = new PipelineStage(TopLevelComponent, "mem_buffer_in", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 64);
            oMemIsolationBufferIn.InputBus.Join(oMemMerger.OutputBus);
            oMemIsolationBufferIn.OutputBus.Join(TopLevelComponent.SDRAMInterface.BusFromInternal);

            //---------------------  Finished Flow PC to SDRAM

            //--------------------- Flow SDRAM to PC

            PipelineStage oMemIsolationBufferOut = new PipelineStage(TopLevelComponent, "mem_buffer_out", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 42);
            oMemIsolationBufferOut.InputBus.Join(TopLevelComponent.SDRAMInterface.BusFromExternal);

            PipelineSorter oMemSorter = new PipelineSorter(TopLevelComponent, "mem_sorter", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 42, 40, 1);
            oMemSorter.InputBus.Join(oMemIsolationBufferOut.OutputBus);
            PipelineBuffer oMemPCBuffer = new PipelineBuffer(TopLevelComponent, "mem_pc_buffer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 42, 4);
            oMemPCBuffer.InputBus.Join(oMemSorter.DefineOutput("PC_O",1));
            PipelineBuffer oMemFPGABuffer = new PipelineBuffer(TopLevelComponent,"mem_fpga_buffer",TopLevelComponent.MainClock,TopLevelComponent.MainReset,42,4);
            oMemFPGABuffer.InputBus.Join(oMemSorter.DefineOutput("FPGA_O",0));

            PipelineMergerPriority oPCMerger = new PipelineMergerPriority(TopLevelComponent, "pc_merger", 42, TopLevelComponent.MainClock, TopLevelComponent.MainReset, 2);
            oPCMerger.InputBuses[0].Join(oMemPCBuffer.OutputBus);
            PipelineBuffer oFPGAPCBuffer = new PipelineBuffer(TopLevelComponent, "fpga_pc", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 42, 2);
            //**PipelineStage oFPGAPCBuffer = new PipelineStage(TopLevelComponent, "fpga_pc", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 42);

            oPCMerger.InputBuses[1].Join(oFPGAPCBuffer.OutputBus);

            PipelineBuffer oPCOutBuffer = new PipelineBuffer(TopLevelComponent, "sdram_buffer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 42, 4);
            //**PipelineStage oPCOutBuffer = new PipelineStage(TopLevelComponent, "pc_ser_buffer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 42);
            oPCOutBuffer.InputBus.Join(oPCMerger.OutputBus);
            //**oPCOutBuffer.InputBus.Join(oFPGAPCBuffer.OutputBus);

            PipelineSerializer oSerializer = new PipelineSerializer(TopLevelComponent, "serializer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 42, 14);
            oSerializer.InputBus.Join(oPCOutBuffer.OutputBus);
            oSerializer.OutputBus.Join(TopLevelComponent.FIFOInterface.BusFromInternal);
            oSerializer.OffsetAddress = true;

            oSNN = new SNNMaster(TopLevelComponent, "SNN", TopLevelComponent.MainClock, TopLevelComponent.MainReset);
            oSNN.InputBuses["EQI"].Join(oPCFPGABuffer.OutputBus);
            oSNN.InputBuses["MEMQI"].Join(oMemFPGABuffer.OutputBus);
            oSNN.OutputBuses["EQO"].Join(oFPGAPCBuffer.InputBus);
            oSNN.OutputBuses["MEMQO"].Join(oFPGAMemBuffer.InputBus);

            //SystemInterface.AddSignalView("PCSorter_IN", oPCSoter.InputBus.Data);
            /*SystemInterface.AddSignalView("FPGA_A", oFPGAPCBuffer.InputBus.Data);
            SystemInterface.AddSignalView("FPGA_B", oFPGAPCBuffer.OutputBus.Data);
            SystemInterface.AddSignalView("FPGA_A_DR", oFPGAPCBuffer.InputBus.DataReady);
            SystemInterface.AddSignalView("FPGA_B_DR", oFPGAPCBuffer.OutputBus.DataReady);

            SystemInterface.AddSignalView("FPGA_A_S", oFPGAPCBuffer.InputBus.Stall);
            SystemInterface.AddSignalView("FPGA_B_S", oFPGAPCBuffer.OutputBus.Stall);

            SystemInterface.AddSignalView("SerBuffer_OUT", oPCOutBuffer.OutputBus.Data);
            SystemInterface.AddSignalView("SerBuffer_OUT_DR", oPCOutBuffer.OutputBus.DataReady);
            SystemInterface.AddSignalView("SerBuffer_OUT_S", oPCOutBuffer.OutputBus.Stall);*/

            return true;
        }

        public override bool ConfigureSignalViews(TopLevelComponent TopLevelComponent)
        {
            //SystemInterface.AddSignalView("LATCH_PIPE", "SNN.pipe_EQO.DATA_READY_I");
            //SystemInterface.AddSignalView("fifo_status", "fifo_ifc.i_status_reg");
            //SystemInterface.AddSignalView("fifo_in", "fifo_ifc.i_fifo_data");

            //SystemInterface.AddSignalView("fifo_in", poTopLevelComponent.FIFOInterface.InputFIFOData,0,14,true);
            //SystemInterface.AddSignalView("fifo_seq", poTopLevelComponent.FIFOInterface.InputFIFOData, 16, 17,true);
            //SystemInterface.AddSignalView("fifo_status", poTopLevelComponent.FIFOInterface.InputStatusReg);
            //SystemInterface.AddSignalView("test", poTopLevelComponent.FIFOInterface.Test);

            /* Last Good SystemInterface.AddSignalView("pipe_data_in", TopLevelComponent.FIFOInterface.BusFromExternal.Data);
            SystemInterface.AddSignalView("pipe_data_in_ready", TopLevelComponent.FIFOInterface.BusFromExternal.DataReady);
            SystemInterface.AddSignalView("pipe_data_out_stall", TopLevelComponent.FIFOInterface.BusFromInternal.Stall);

            SystemInterface.AddSignalView("pipe_data_out", TopLevelComponent.FIFOInterface.BusFromInternal.Data);
            SystemInterface.AddSignalView("pipe_data_out_ready", TopLevelComponent.FIFOInterface.BusFromInternal.DataReady);
            SystemInterface.AddSignalView("pipe_data_in_stall", TopLevelComponent.FIFOInterface.BusFromExternal.Stall);

            SystemInterface.AddSignalView("sdram_out", TopLevelComponent.SDRAMInterface.BusFromExternal.Data);
            SystemInterface.AddSignalView("sdram_ready", TopLevelComponent.SDRAMInterface.BusFromExternal.DataReady);
            SystemInterface.AddSignalView("sdram_stall", TopLevelComponent.SDRAMInterface.BusFromExternal.Stall);*/

            /*SystemInterface.AddSignalView("state_reset", oAssembler.oStateMachine.StateCurrent("state_reset"));
            SystemInterface.AddSignalView("state_0", oAssembler.oStateMachine.StateCurrent("state_0"));
            SystemInterface.AddSignalView("state_1", oAssembler.oStateMachine.StateCurrent("state_1"));
            SystemInterface.AddSignalView("state_2", oAssembler.oStateMachine.StateCurrent("state_2"));
            SystemInterface.AddSignalView("state_final", oAssembler.oStateMachine.StateCurrent("state_final"));
            SystemInterface.AddSignalView("address", oAssembler.nvAddress);*/

            oSNN.AddSignalViews();

            return true;
        }

        public void SigalViews(HardwareScript poScript, int piTime)
        {
           /* poScript.Echo(piTime, "======================================= Time" + piTime.ToString());
            poScript.SignalView(piTime, "HELLO");
            poScript.Echo(piTime, "FIFOS");
            poScript.SignalView(piTime, "LATCH_PIPE");
            
            poScript.SignalView(piTime, "FPGA_A");
            poScript.SignalView(piTime, "FPGA_B");
            poScript.SignalView(piTime, "FPGA_A_DR");
            poScript.SignalView(piTime, "FPGA_B_DR");

            poScript.SignalView(piTime, "FPGA_A_S");
            poScript.SignalView(piTime, "FPGA_B_S");

            poScript.SignalView(piTime, "SerBuffer_OUT");
            poScript.SignalView(piTime, "SerBuffer_OUT_DR");
            poScript.SignalView(piTime, "SerBuffer_OUT_S");*/
        }

       /* public void ShowFIFO(HardwareScript poScript, int piTime)
        {
            poScript.Echo(piTime, "======================================= Time" + piTime.ToString());
            poScript.SignalView(piTime, "pipe_data_in");
            poScript.SignalView(piTime, "pipe_data_in_ready");
            poScript.Echo(piTime, "--");
            poScript.SignalView(piTime, "pipe_data_out");
            poScript.SignalView(piTime, "pipe_data_out_ready");
            poScript.Echo(piTime, "...");
            poScript.SignalView(piTime, "sdram_out");
            poScript.SignalView(piTime, "sdram_ready");
            poScript.SignalView(piTime, "sdram_stall");
            poScript.SignalView(piTime, ".TEST_BUS_READ_I", SignalFormat.Binary);

            /*poScript.SignalView(piTime, "address");            
            poScript.SignalView(piTime, "state_reset");
            poScript.SignalView(piTime, "state_0");
            poScript.SignalView(piTime, "state_1");
            poScript.SignalView(piTime, "state_2");
            poScript.SignalView(piTime, "state_final");///
        }*/

        public override bool GenerateHardwareScripts()
        {
            HardwareScript oScript = HardwareScripts.NewScript("test");
            oScript.NoReset = true;

            for (int iTime = 0; iTime < 30; iTime++)
            {
                SigalViews(oScript, iTime);
                oSNN.SignalDisplay(oScript, iTime);

            }
            return true;
        }

        public static void PCTest()
        {
            const int BLOCKS = 4;

            MemoryBlock oMemoryBlock = new MemoryBlock();

            for (int iX = 0; iX < BLOCKS; iX++)
                oMemoryBlock.WriteMemory(iX + 16, (uint)iX + 8);

            IOInterface.PostMemory(ref oMemoryBlock);
            if (!oMemoryBlock.IsReady)
            {
                System.Windows.Forms.MessageBox.Show("Failed Writing \n" + oMemoryBlock.DebugDisplay());
                return;
            }

            oMemoryBlock.Reset();
            for (int iX = 0; iX < BLOCKS; iX++)
                oMemoryBlock.RequestMemory(iX + 16);

            IOInterface.PostMemory(ref oMemoryBlock);
            if (!oMemoryBlock.IsReady)
            {
                System.Windows.Forms.MessageBox.Show("Failed Reading \n" + oMemoryBlock.DebugDisplay());
                return;
            }

            oMemoryBlock.Reset();

            for (int iX = 0; iX < BLOCKS; iX++)
                oMemoryBlock.WriteMemory(iX + 10000, (uint)iX);

            IOInterface.PostMemory(ref oMemoryBlock);
            if (!oMemoryBlock.IsReady)
            {
                System.Windows.Forms.MessageBox.Show("Failed Writing Second Block \n" + oMemoryBlock.DebugDisplay());
                return;
            }

            oMemoryBlock.Reset();
            oMemoryBlock.RequestMemory(16);
            oMemoryBlock.RequestMemory(10001);
            oMemoryBlock.RequestMemory(18);
            oMemoryBlock.RequestMemory(10003);

            IOInterface.PostMemory(ref oMemoryBlock);
            if (!oMemoryBlock.IsReady)
            {
                System.Windows.Forms.MessageBox.Show("Failed Reading Second Block \n" + oMemoryBlock.DebugDisplay());
                return;
            }




            System.Windows.Forms.MessageBox.Show(oMemoryBlock.DebugDisplay());
        }

        public SNNMaster SNN
        {
            get { return oSNN; } 
        }
    }
}


